package com.project.myproject.字节;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * @author Honorable(mabingqian)
 * @description 手写 jdk 7 的HashMap的 get和put操作， hashMap采用的是头插法；
 * @date 2020/7/16 18:06
 * @update （更新、优化，注明点）
 */
public class MyHashMap {

    Entry[] table;

    int DEFAULT_CAPACITY = 16;
    float loadFactor = 0.75f;
    int threshod;
    int size;

    volatile int modCount = 0;

    /**
     * HashMap的 get操作
     * @param key
     * @return
     */
    public Integer get(Integer key) {
        if (key == null) {
            return getForNullKey();
        }

        if (size == 0) {
            return null;
        }

        int hash = key.hashCode();
        int index = getIndex(hash, table);

        for (Entry e = table[index]; e != null; e = e.next) {
            if (e.hash == hash && (e.key == key || e.key.equals(key))) {
                return e.value;
            }
        }
        return null;

    }


    /**
     * 如果key == null，按照规定key为null的情况，都存储到了table[0]的位置
     * @return
     */
    Integer getForNullKey() {
        Entry entry = table[0];
        while (entry.next == null) {
            return entry.value;
        }
        return null;
    }

    /**
     * hashMap的put方法
     * @param key
     * @param value
     * @return
     */
    public Integer put(Integer key, Integer value) {
        if (table == null) {
            initTable();
        }
        //如果key值为null，则统一加入到table[0]位置；
        if (key == null) {
            return putForNullKey(value);
        }
        int hash = key.hashCode();
        int indexFor = getIndex(hash, table);
        for (Entry entry = table[indexFor]; entry != null; entry = entry.next) {
            Object k;
            if (entry.hash == hash && ((k = entry.key) == key || key.equals(k))) {
                Integer oldValue = entry.value;
                entry.value = value;
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, indexFor);
        return null;
    }

    public void  resize(int size) {

    }

    public void addEntry(int hash, Integer key, Integer value, int indexFor) {
        if (size >= threshod && null != table[indexFor]) {
            //扩容 2倍
            resize(2 * size);
            //扩容后重新计算hash值
            hash = key != null ? key.hashCode() : 0;
            //重新计算索引
            indexFor = getIndex(hash, table);
        }

        createEntry(hash, key, value, indexFor);
    }

    public void createEntry(int hash, Integer key, Integer value, int indexFor) {
        Entry entry = table[indexFor];
        //头插法
        table[indexFor] = new Entry(hash, key, value, entry);
        size++;
    }


    public int getIndex(int hash, Entry[] table) {
        if (table == null) {
            throw new RuntimeException("table not init");
        }
        if (hash == 0) {
            return 0;
        }
        return hash ^ (table.length - 1);

    }


    public int putForNullKey(Integer value) {
        if (table.length > 0) {
            Entry entry = table[0];
            Entry newData = new Entry(0, null, value, null);
            if (entry != null) {
                while (entry.next == null) {
                    entry.next = newData;
                    newData.next = null;
                    return value;
                }
            }
            table[0] = entry;
            entry.next = null;
            return value;
        }
        throw new RuntimeException("table not init");
    }


    public void initTable() {
        //计算容量
        int capacity = DEFAULT_CAPACITY;
        //计算扩容的阈值
        this.threshod = (int) (capacity * loadFactor);
        //初始化table
        table = new Entry[capacity];
    }

    public int size() {
        return size;
    }

}


@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
class Entry {
    int hash;
    Integer key;
    Integer value;
    Entry next;

}

class MapMain {
    public static void main(String[] args) {
        MyHashMap map = new MyHashMap();
        map.put(1, 2);
        System.out.println(map.size());

        Integer integer = map.get(1);
        System.out.println(integer);
    }
}
